/* FileSaver.js
 * A saveAs() FileSaver implementation.
 * 2013-01-23
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See LICENSE.md
 * adapted to proALPHA by jz
 */

/*global self, define, navigator, saveAs */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */

define([
], function () {

    var prototype, get_URL, click, throw_outside, process_deletion_queue, dispatch,
        factory, saveAs,
        view = this,
        doc = view.document,
        URL = view.URL || view.webkitURL || view,
        save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"),
        can_use_save_link =  !view.externalHost && "download" in save_link,
        webkit_req_fs = view.webkitRequestFileSystem,
        req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem,
        force_saveable_type = "application/octet-stream",
        fs_min_size = 0,
        deletion_queue = [];
        
    // =========================================================================
    // The object prototype
    // =========================================================================    

    prototype = {
        readyState: 0,
        INIT: 0,
        WRITING: 1,
        DONE: 2,
        error: null,
        onwritestart: null,
        onprogress: null,
        onwrite: null,
        onabort: null,
        onerror: null,
        onwriteend: null,
        abort: function () {
            var filesaver = this;
            filesaver.readyState = filesaver.DONE;
            dispatch(filesaver, "abort");
        }
    };        
    
    // =========================================================================
    // Functions
    // ========================================================================= 
    
    throw_outside = function (ex) {
        var timeout = view.setImmediate || view.setTimeout;
        timeout(function () {
            throw ex;
        }, 0);
    };        
        
    dispatch = function (filesaver, event_types, event) {
        var i, listener;
        event_types = [].concat(event_types);
        i = event_types.length;
        while (i) {
            i = i - 1;
            listener = filesaver["on" + event_types[i]];
            if (typeof listener === "function") {
                try {
                    listener.call(filesaver, event || filesaver);
                } catch (ex) {
                    throw_outside(ex);
                }
            }
        }
    };
    
    click = function (node) {
        var event = doc.createEvent("MouseEvents");
        event.initMouseEvent(
            "click", true, false, view, 0, 0, 0, 0, 0,
            false, false, false, false, 0, null
        );
        node.dispatchEvent(event);
    };    
    
    // only get URL when necessary in case BlobBuilder.js hasn't overridden it yet

    get_URL = function () {
        return view.URL || view.webkitURL || view;
    };
        
    process_deletion_queue = function () {
        var file,
            i = deletion_queue.length;
        
        while (i) {
            i = i - 1;
            file = deletion_queue[i];
            if (typeof file === "string") { // file is an object URL
                URL.revokeObjectURL(file);
            } else { // file is a File
                file.remove();
            }
        }
        deletion_queue.length = 0; // clear queue
    };  
    
    // =========================================================================
    // factory function
    // =========================================================================
    
    factory = function (blob, name) {
        var filesaver, object_url, target_view, get_object_url, dispatch_all,
            fs_error, window, abortable,  slice,
            create_if_not_found = {
                create: true, 
                exclusive: false
            },
            type         = blob.type,
            blob_changed = false;
            
        filesaver = Object.create(prototype);
        
        get_object_url = function () {
            var object_url = get_URL().createObjectURL(blob);
            deletion_queue.push(object_url);
			return object_url;
		};
		
		dispatch_all = function () {
		    dispatch(filesaver, "writestart progress write writeend".split(" "));
		};        

        // on any filesys errors revert to saving with object URLs
        fs_error = function () {
            // don't create more object URLs than needed
            if (blob_changed || !object_url) {
                object_url = get_object_url(blob);
            }
            if (target_view) {
                target_view.location.href = object_url;
            } else {
                window.open(object_url, "_blank");
            }
            filesaver.readyState = filesaver.DONE;
            dispatch_all();
        };
        
        abortable = function (func) {
            return function () {
                if (filesaver.readyState !== filesaver.DONE) {
                    return func.apply(this, arguments);
                }
            };
        }; 
        
        filesaver.readyState = filesaver.INIT;
        if (!name) {
            name = "download";
        }
        if (can_use_save_link) {
            object_url = get_object_url(blob);
            save_link.href = object_url;
            save_link.download = name;
            click(save_link);
            filesaver.readyState = filesaver.DONE;
            dispatch_all();
            return;
        }       
        
        // Object and web filesystem URLs have a problem saving in Google Chrome when
        // viewed in a tab, so I force save with application/octet-stream
        // http://code.google.com/p/chromium/issues/detail?id=91158
        if (view.chrome && type && type !== force_saveable_type) {
            slice = blob.slice || blob.webkitSlice;
            blob = slice.call(blob, 0, blob.size, force_saveable_type);
            blob_changed = true;
        }
        // Since I can't be sure that the guessed media type will trigger a download
        // in WebKit, I append .download to the filename.
        // https://bugs.webkit.org/show_bug.cgi?id=65440
        if (webkit_req_fs && name !== "download") {
            name += ".download";
        }
        if (type === force_saveable_type || webkit_req_fs) {
            target_view = view;
        }
        if (!req_fs) {
            fs_error();
            return;
        }
        fs_min_size += blob.size;
        req_fs(view.TEMPORARY, fs_min_size, abortable(function (fs) {
            fs.root.getDirectory("saved", create_if_not_found, abortable(function (dir) {
                var save = function () {
                    dir.getFile(name, create_if_not_found, abortable(function (file) {
                        file.createWriter(abortable(function (writer) {
                            writer.onwriteend = function (event) {
                                target_view.location.href = file.toURL();
                                deletion_queue.push(file);
                                filesaver.readyState = filesaver.DONE;
                                dispatch(filesaver, "writeend", event);
                            };
                            writer.onerror = function () {
                                var error = writer.error;
                                if (error.code !== error.ABORT_ERR) {
                                    fs_error();
                                }
                            };
                            "writestart progress write abort".split(" ").forEach(function (event) {
                                writer["on" + event] = filesaver["on" + event];
                            });
                            writer.write(blob);
                            filesaver.abort = function () {
                                writer.abort();
                                filesaver.readyState = filesaver.DONE;
                            };
                            filesaver.readyState = filesaver.WRITING;
                        }), fs_error);
                    }), fs_error);
                };
                dir.getFile(name, {create: false}, abortable(function (file) {
                    // delete file if it already exists
                    file.remove();
                    save();
                }), abortable(function (ex) {
                    if (ex.code === ex.NOT_FOUND_ERR) {
                        save();
                    } else {
                        fs_error();
                    }
                }));
            }), fs_error);
        }), fs_error);
        
        return filesaver;
    
    };
    
    saveAs = saveAs || (navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) || function (blob, name) {
        return factory(blob, name); 
    };  
    
    return saveAs; 
    
});
